#ifndef _PLAYER_H
#define _PLAYER_H

#pragma pack(1)

struct CharRaceEntry
{
	ui32 race_id;
	ui32 team_id;
	ui32 cinematic_id;
	char* name1;
};

struct CharClassEntry
{
	ui32 class_id;
	ui32 power_type;
	char* name;
};

struct Button
{
	ui16 action;
	ui8 type;
	ui8 misc;
};

enum MoveTypes
{
	TYPE_ROOT		= 0x00,
	TYPE_UNROOT		= 0x01,
	NUM_MOVETYPES,
};

struct LfgInfo
{
	ui16 type;
	ui16 id;
};

#pragma pack()

class GossipMenu;

class DLL_EXPORT Player : public Unit
{
	friend class Session;
	friend class MapHandler;
	ui64 guid;
	Session* m_session;
	CharRaceEntry* m_race;
	CharClassEntry* m_class;
	PlayerInfo* m_playerinfo;
	const char* m_name;
	ui32 _fields[PLAYER_END];
	ui32 m_banned;
	ui8 m_level;
	ui32 m_zoneid;
	ui32 m_guildid;
	bool m_isfemale;
	bool m_loginfailed;
	bool m_loaded;
	bool m_timeresync;
	bool m_binddialog;
	bool m_firstLogin;
	ItemInterface* m_interface;
	map<ui32, SkillLineEntry*> m_skills;
	set<ui32> m_spells;
	set<ui32> m_finishedquests;
	ui32 m_armorprof, m_weaponprof;
	ui32 m_areaid;
	ui64 m_selectedguid;
	ui32 m_creationcount;
	ui32 m_updatecount;
	ui32 m_lastupdatetime;
	DataBuffer m_updatebuffer;
	DataBuffer m_creationbuffer;
	Mutex m_outmutex;
	RealmPacket m_outofranges;
	ui32 m_outofrangecount;
	Landmark m_bindpoint;
	ui32 m_bindzone;
	Button m_actions[120];
	bool transferpending;
	ui32 last_cast_number;
	TaxiPath* currentpath;
	vector<TaxiPath*> m_currentexpress;
	vector<TaxiPathNode*> m_expressway;
	Mutex expresslock;
	Spell* m_currentspell;
	LfgInfo m_lfginfo[3];

	void __AddSkillLine(ui32 id, ui32 curlvl, ui32 max_level);
	void __UpdateSkills();
	void Login();
	void SendInitialPackets();
	void SendInitialSpells();
	void SendInitialActions();
	void LoadItems();

	void CreateInRangeSet();
	void AddInRangeObject(Object* obj);
	void PushOutOfRange(Object* obj);
	void SendCompressed(RealmPacket& data);
	void ClearUpdateData();
	void ClearAllInrange();
	ui32 CalculateUpdateSize();
	void LogOut();
	bool Update(ui32 time);

	void Regenerate();

	ui32 m_questslots[25];
public:

	Player(ui64 GUID);

	void SetSession(Session* session)
	{
		m_session = session;
	}

	void CreateNew(RealmPacket& pack, PlayerCreateInfo* inf);

	Session* GetSession() { return m_session; }

	void SaveToDb();
	void LoadFromDb();
	__inline const char* GetName()
	{
		return m_name;
	}

	ui32 m_taximask[12];
	ui32 GetFreeCastNumber() { if(last_cast_number == 255) return (last_cast_number = 1); return ++last_cast_number; }
	bool GetTaxiStatus(ui8 pos, ui32& out)
	{
		if(pos > 11)
			return false;
		out = m_taximask[pos];
		return true;
	}
	void SetTaxiStatus(ui8 pos, ui32 value)
	{
		if(pos > 11)
			return;
		m_taximask[pos] = value;
	}
	void StartTaxi(TaxiPath* path, ui32 displayid);

	LfgInfo GetLfgInfo(ui8 pos) 
	{ 
		if(pos < 3) 
			return m_lfginfo[pos]; 
		throw("Out of range!"); 
	}

	void SetLfgInfo(ui8 pos, ui16 type, ui16 id) 
	{ 
		if(pos < 3)
		{
			m_lfginfo[pos].type = type;
			m_lfginfo[pos].id = id;
		}
	}

	ItemInterface* GetItemInterface() { return m_interface; }

	void RedSystemMessage(const char* msg, ...);
	void GreenSystemMessage(const char* msg, ...);
	void BlueSystemMessage(const char* msg, ...);
	void SystemMessage(const char* msg, ...);

	void AddSpell(ui32 id);
	void LogEnvironmentalDamage(ui32 dmg, ui8 type);
	const ui64& GetSelection() const { return m_selectedguid; }
	Unit* GetSelectedUnit();
	Creature* GetSelectedCreature();
	Player* GetSelectedPlayer();
	void SetSelection(ui64 guid) { m_selectedguid = guid; }
	void PushCreationData(RealmPacket* data);
	void PushUpdateData(RealmPacket* data);
	void ProcessUpdates(bool lockcreation = true, bool lockupdate = true);
	void TurnFlyMode(bool on);

	GossipMenu* m_openedmenu;
	void GossipEnd();
	void SetNewBindPoint(float x, float y, float z, ui32 mapid, ui32 zoneid, Creature* innkeeper = NULL);
	void SendGossipPoi(float x, float y, ui32 icon, ui32 flags, char* name);

	void SetStandState(ui8 state);
	void SetActionButton(ui8 pos, ui16 action, ui8 type, ui8 misc)
	{
		if(pos >= 112)
			return;
		m_actions[pos].action = action;
		m_actions[pos].type = type;
		m_actions[pos].misc = misc;
	}

	bool HasSpell(ui32 id)
	{
		return m_spells.find(id) != m_spells.end();
	}

	//	TeleportTo doesnt care about the stuff needed to enter the place (like minlevel of the map, ...)
	void TeleportTo(Landmark pos, float orientation = 1.0f);
	void TeleportTo(float x, float y, float z, ui32 mapid, float orientation = 1.0f);

	//  CheckedTeleport checks if the player can enter and only ports if possible
	void CheckedTeleportTo(Landmark pos, float orientation = 1.0f);

	inline ui32 GetLevel() const { return GetUI32Value(UNIT_FIELD_LEVEL); }
	inline void SetLevel(ui32 newlevel) { SetUI32Value(UNIT_FIELD_LEVEL, newlevel); }
	inline ui32 GetMoney() const { return GetUI32Value(PLAYER_FIELD_COINAGE); }
	inline void SetMoney(ui32 newmoney) { SetUI32Value(PLAYER_FIELD_COINAGE, newmoney); }
	inline void SubtractMoney(Useable::Money& amount) { SetMoney(GetMoney() - Useable::ConvertToCopper(amount)); }
	inline void SubtractMoney(ui32 copperamount) { SetMoney(GetMoney() - copperamount); }
	inline void AddMoney(Useable::Money& amount) { SetMoney(GetMoney() + Useable::ConvertToCopper(amount)); }
	inline void AddMoney(ui32 copperamount) { SetMoney(GetMoney() + copperamount); }
	inline ui8 GetTeam()
	{
		return m_race->race_id;
	}
	inline bool GetHorde()
	{
		switch(m_race->race_id)
		{
		case 1:
		case 4:
		case 8:
		case 64:
		case 1024:
			return false;
		default:
			return true;
		}
	}

	inline bool HasQuestFinished(ui32 questid) 
	{ 
		if(!questid) 
			return true; 
		else 
			return m_finishedquests.find(questid) != m_finishedquests.end(); 
	}

	inline void SetQuest(ui32 id, ui32 slot)
	{
		m_questslots[slot] = id;
	}

	ui8 AddQuest(ui32 id);

	inline void UpdateQuest(ui32 slot)
	{
		ui32 basefield = PLAYER_QUEST_LOG_1_1 + 4 * slot;
		SetUI32Value(basefield, m_questslots[slot]);
		SetUI32Value(basefield + 1, 0);
		SetUI32Value(basefield + 2, 0);
	}

	bool IsAtQuest(ui32 id)
	{
		bool has = false;
		for(register ui32 i = 0; i < 25; ++i)
			if(m_questslots[i] == id)
				has = true;
		return has;
	}

	ui32 GetAreaId() { return m_areaid; }
	bool CanUse(char cmd) { return m_session->CanUseCommand(cmd); }

	CombatHandler m_combathandler;
	void Root();
	//	Roots the player <time> milliseconds
	void Root(ui32 time);
	void UnRoot();
	//	Unroots the player <time> milliseconds
	void UnRoot(ui32 time);

	void SetMoveType(MoveTypes type, ui32 value);
	void EventTaxiEnd();
	void EventTaxiUpdate(ui32 start);
	__forceinline void AddExpressPath(TaxiPath* exp) { expresslock.Acquire(); m_currentexpress.push_back(exp); expresslock.Release(); }
	__forceinline void ClearExpressWay() { expresslock.Acquire(); m_currentexpress.clear(); expresslock.Release(); }
	void StartExpressTaxi(ui32 taxidisplayid);
	void EventExpressEnd();
	void EventExpressUpdate(ui32);
	void SetCurrentSpell(Spell* sp) { m_currentspell = sp; }
	Landmark GetBindPoint() { return m_bindpoint; }
	void SendSpellError(ui8 errornumber, ui32 spellid);
	GameObject* m_selectedGo;
};

#endif